package com.hui.platform.mybatisplusgenerator.devops.generator;

import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.hui.platform.mybatisplusgenerator.devops.constant.GenFileTypeEnum;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * 代码生成器
 * 说明:开发工具为IDEA，暂不支持Eclipse
 *
 * @author Peng
 * @date 2019/10/15
 */
@Slf4j
@Data
@Accessors(chain = true)
public class CodeGenerator {

    /**
     * 需要修改的值
     * author:作者
     * moduleName:模块名，小写，注意更新表字段的时候，模块与表所在模块需要对应
     * moduleDesc:模块描述
     * includeTables:需要生成的表
     */
    private String author = "hui";
    private String moduleName = "test";
    private String moduleDesc = "测试模块";
    private String[] includeTables = {};

    /**
     * 是否覆盖原有文件，默认false
     */
    private boolean fileOverride = false;

    /**
     * 固定模块值
     */
    private String appPackage = "com.hui.platform.falseworkapi";
    private String moduleParent = appPackage + ".service.business";

    private String projectPath = System.getProperty("user.dir")+"/mybatisplus-generator";
    private String logicDeleteFieldName = "deleted";

    /**
     * 数据库配置 ymtimer
     * TODO 初始配置
     */
    private String dbUrl = "初始配置";
    private String dbUsername = "初始配置";
    private String dbPassword = "初始配置";
    private String dbDriverName = "com.mysql.cj.jdbc.Driver";

    /**
     * 获取模板配置变量
     *
     * @return 模板配置变量
     */
    private Map<String, Object> getTemplateValMap() {
        HashMap<String, Object> temMap = new HashMap<>(8);
        //应用
        temMap.put("AppPackage", appPackage);
        //模块
        temMap.put("ModulePackage", moduleParent + "." + moduleName);
        temMap.put("ModuleName", moduleName);
        temMap.put("ModuleDesc", moduleDesc);
        //包名
        temMap.put("MybatisConfigPackageName", GenFileTypeEnum.MYBATIS_CONFIG_FILE.getRelativePackageName());
        temMap.put("SwaggerConfigPackageName", GenFileTypeEnum.SWAGGER_CONFIG_FILE.getRelativePackageName());
        temMap.put("BusinessErrorEnumPackageName", GenFileTypeEnum.BUSINESS_ERROR_ENUM_FILE.getRelativePackageName());
        temMap.put("DtoPackageName", GenFileTypeEnum.ENTITY_DTO_FILE.getRelativePackageName());
        temMap.put("BoPackageName", GenFileTypeEnum.ENTITY_BO_FILE.getRelativePackageName());
        temMap.put("VoPackageName", GenFileTypeEnum.ENTITY_VO_FILE.getRelativePackageName());
        temMap.put("ConvertPackageName", GenFileTypeEnum.ENTITY_CONVERT_FILE.getRelativePackageName());
        //后缀名
        temMap.put("EntitySuffix", GenFileTypeEnum.ENTITY_DO_FILE.getSuffix());
        temMap.put("BoSuffix", GenFileTypeEnum.ENTITY_BO_FILE.getSuffix());
        temMap.put("DtoSuffix", GenFileTypeEnum.ENTITY_DTO_FILE.getSuffix());
        temMap.put("VoSuffix", GenFileTypeEnum.ENTITY_VO_FILE.getSuffix());
        temMap.put("ConvertSuffix", GenFileTypeEnum.ENTITY_CONVERT_FILE.getSuffix());
        temMap.put("MybatisConfigName", GenFileTypeEnum.MYBATIS_CONFIG_FILE.getSuffix());
        temMap.put("SwaggerConfigName", GenFileTypeEnum.SWAGGER_CONFIG_FILE.getSuffix());
        temMap.put("BusinessErrorEnumName", GenFileTypeEnum.BUSINESS_ERROR_ENUM_FILE.getSuffix());

        //DTO，BO，VO，Controller是否开启Swagger
        temMap.put("IsSwagger2", true);

        return temMap;
    }

    /**
     * 代码生成Build方法
     */
    public void build() {
        /*
         * 代码生成器
         * 配置参考官网-MybatisPlus-代码生成配置
         * https://mp.baomidou.com/config/generator-config.html
         */
        AutoGenerator mpg = new AutoGenerator()
                //1.数据源配置
                .setDataSource(getDataSourceConfig())
                //2.包名配置
                .setPackageInfo(getPackageConfig())
                //3.模板配置
                .setTemplate(getTemplateConfig())
                //4.全局策略 globalConfig 配置
                .setGlobalConfig(getGlobalConfig())
                //5.数据库表配置-策略配置
                .setStrategy(getStrategyConfig())
                //6.注入 injectionConfig 配置
                .setCfg(getInjectionConfig())
                //设置模板引擎
                .setTemplateEngine(new FreemarkerTemplateEngine());

        //执行生成
        mpg.execute();
    }

    /**
     * 1.数据源配置
     */
    private DataSourceConfig getDataSourceConfig() {
        DataSourceConfig dsc = new DataSourceConfig();
        return dsc
                .setUrl(dbUrl)
                .setDriverName(dbDriverName)
                .setUsername(dbUsername)
                .setPassword(dbPassword);
    }

    /**
     * 2.包名配置
     */
    private PackageConfig getPackageConfig() {
        PackageConfig pc = new PackageConfig();
        return pc
                //设置模块名
                .setModuleName(moduleName)
                //设置父包
                .setParent(moduleParent)
                //xml 文件与mapper同包
                .setXml(pc.getMapper());
    }

    /**
     * 3.模板配置
     */
    private TemplateConfig getTemplateConfig() {
        TemplateConfig tc = new TemplateConfig();
        if (!GenFileTypeEnum.ENTITY_DO_FILE.isEnabled()) {
            tc.setEntity(null);
        }
        if (!GenFileTypeEnum.MAPPER_FILE.isEnabled()) {
            tc.setMapper(null);
        }
        if (!GenFileTypeEnum.MAPPER_XML_FILE.isEnabled()) {
            tc.setXml(null);
        }
        if (!GenFileTypeEnum.SERVICE_FILE.isEnabled()) {
            tc.setService(null);
        }
        if (!GenFileTypeEnum.SERVICE_IMPL_FILE.isEnabled()) {
            tc.setServiceImpl(null);
        }
        if (!GenFileTypeEnum.CONTROLLER_FILE.isEnabled()) {
            tc.setController(null);
        }
        return tc;
    }

    /**
     * 4.全局策略 globalConfig 配置
     */
    private GlobalConfig getGlobalConfig() {
        GlobalConfig gc = new GlobalConfig();
        return gc
                //作者
                .setAuthor(author)
                //生成目录
                .setOutputDir(projectPath + "/src/main/java")
                // 文件覆盖
                .setFileOverride(fileOverride)
                //输出目录：默认D://
                .setOpen(false)
                .setServiceName("%sService")
                .setEntityName("%s" + GenFileTypeEnum.ENTITY_DO_FILE.getSuffix())
                //开启实体属性 Swagger2 注解，DO不需要swagger的东西
                .setSwagger2(false)
                //设置时间类型-希望采用默认的JDK8的时间类型-可是XXX TODO 希望采用JDK8 的localdatetime
                .setDateType(DateType.ONLY_DATE);
    }

    /**
     * 5.数据库表配置-策略配置
     */
    private StrategyConfig getStrategyConfig() {
        //检查表是否为空
        Assert.notEmpty(includeTables,"tables must not empty");
        StrategyConfig strategy = new StrategyConfig();
        return strategy
                //设置表名命名规则-下换线转大写
                .setNaming(NamingStrategy.underline_to_camel)
                //设置列名命名规则-下划线转大写
                .setColumnNaming(NamingStrategy.underline_to_camel)
                //设置Entity模式-Lombok
                .setEntityLombokModel(true)
                //设置RestController而非Controller
                .setRestControllerStyle(true)
                //生成的表，可以多选
                .setInclude(includeTables)
                //设置静态字段-用于拼接sql
                .setEntityColumnConstant(true)
                //设置Controller的url为大写转-
                .setControllerMappingHyphenStyle(true)
                //设置Entity的Mybatis-Plus的DO的注解
                .setEntityTableFieldAnnotationEnable(true)
                //设置逻辑删除的字段值名称
                .setLogicDeleteFieldName(logicDeleteFieldName)
                ;
    }

    /**
     * 6.注入 injectionConfig 配置
     */
    private InjectionConfig getInjectionConfig() {
        //构建自定义注入配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                setMap(getTemplateValMap());
            }
        };
        // 自定义输出文件配置
        List<FileOutConfig> focList = new ArrayList<>();

        // Mybatis包扫描配置文件
        if (GenFileTypeEnum.MYBATIS_CONFIG_FILE.isEnabled()) {
            focList.add(getFileOutConfig(GenFileTypeEnum.MYBATIS_CONFIG_FILE, StringUtils.capitalize(moduleName), false));
        }
        // Swagger文档配置文件
        if (GenFileTypeEnum.SWAGGER_CONFIG_FILE.isEnabled()) {
            focList.add(getFileOutConfig(GenFileTypeEnum.SWAGGER_CONFIG_FILE, StringUtils.capitalize(moduleName), false));
        }
        // BusinessErrorEnum枚举文件
        if (GenFileTypeEnum.BUSINESS_ERROR_ENUM_FILE.isEnabled()) {
            focList.add(getFileOutConfig(GenFileTypeEnum.BUSINESS_ERROR_ENUM_FILE, StringUtils.capitalize(moduleName), false));
        }
        // Entity的DTO
        if (GenFileTypeEnum.ENTITY_DTO_FILE.isEnabled()) {
            focList.add(getFileOutConfig(GenFileTypeEnum.ENTITY_DTO_FILE, null, true));
        }
        // Entity的BO
        if (GenFileTypeEnum.ENTITY_BO_FILE.isEnabled()) {
            focList.add(getFileOutConfig(GenFileTypeEnum.ENTITY_BO_FILE, null, true));
        }
        // Entity的VO
        if (GenFileTypeEnum.ENTITY_VO_FILE.isEnabled()) {
            focList.add(getFileOutConfig(GenFileTypeEnum.ENTITY_VO_FILE, null, true));
        }
        // Entity的Convert
        if (GenFileTypeEnum.ENTITY_CONVERT_FILE.isEnabled()) {
            focList.add(getFileOutConfig(GenFileTypeEnum.ENTITY_CONVERT_FILE, null, true));
        }
        //设置配置文件列表
        cfg.setFileOutConfigList(focList);
        return cfg;
    }

    /**
     * 获取 FileOutConfig 文件配置
     *
     * @param fileType       GenFileTypeEnum的枚举类型
     * @param fileNamePrefix 文件名前缀
     * @param isMultiFile    是否多文件（一张表一个文件）
     * @return FileOutConfig
     */
    private FileOutConfig getFileOutConfig(GenFileTypeEnum fileType, String fileNamePrefix, boolean isMultiFile) {
        return new FileOutConfig(fileType.getTemplatePath()) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                //文件名
                String fileName = "";
                //判断是否多文件（模块多个文件，一个表一个）
                if (isMultiFile) {
                    /*
                     * 默认生成策略：使用Entity的名字去掉后缀
                     */
                    fileName = tableInfo.getEntityName()
                            .substring(0, tableInfo.getEntityName().length() - GenFileTypeEnum.ENTITY_DO_FILE.getSuffix().length());
                }
                return projectPath + "/src/main/java/"
                        + moduleParent.replaceAll("\\.", "/") + "/" + moduleName + "/"
                        //相对包名
                        + fileType.getRelativePackageName() + "/"
                        //文件前缀
                        + Optional.ofNullable(fileNamePrefix).orElse("")
                        //文件名
                        + fileName
                        //文件名后缀
                        + fileType.getSuffix()
                        //文件后缀
                        + fileType.getFileSuffix();
            }
        };
    }

}
